package webhandler

import (
	"fmt"
	"net/http"
	"sync"
	"time"
)

type Method string

const (
	POST    Method = "POST"
	GET     Method = "GET"
	PUT     Method = "PUT"
	DELETE  Method = "DELETE"
	PATCH   Method = "PATCH"
	OPTIONS Method = "OPTIONS"
)

type methodPath struct {
	method Method
	path   string
}

type WebContainerFilter func(handlerFunc webHandlerFunc)

type WebContainer interface {
	POST(path string, webHandler webHandlerFunc)
	GET(path string, webHandler webHandlerFunc)
	PUT(path string, webHandler webHandlerFunc)
	DELETE(path string, webHandler webHandlerFunc)

	ListenAndServe() error
	ListenAndServeTLS(certFile, keyFile string) error
}

type webHandlerFunc func(WebContext)

func NewWebContainer(server http.Server) WebContainer {
	wc := &webContainer{
		routes: map[methodPath]webHandlerFunc{},
		server: server,
	}
	wc.wh = &simpleHandlerWrapper{wc: wc}
	return wc
}

func NewDefaultWebContainer(addr string) WebContainer {
	return NewWebContainer(http.Server{Addr: addr})
}

type webContainer struct {
	locker sync.Mutex
	routes map[methodPath]webHandlerFunc
	server http.Server
	wh     *simpleHandlerWrapper
}

func (wc *webContainer) POST(path string, whf webHandlerFunc) {
	wc.addRoute(POST, path, whf)
}

func (wc *webContainer) GET(path string, whf webHandlerFunc) {
	wc.addRoute(GET, path, whf)
}
func (wc *webContainer) PUT(path string, whf webHandlerFunc) {
	wc.addRoute(PUT, path, whf)
}
func (wc *webContainer) DELETE(path string, whf webHandlerFunc) {
	wc.addRoute(DELETE, path, whf)
}

func (wc *webContainer) ListenAndServe() error {
	wc.locker.Lock()
	defer wc.locker.Unlock()
	wc.server.Handler = http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
		wc.wh.handler(WebContext{request: request, response: writer})
	})
	return wc.server.ListenAndServe()
}

func (wc *webContainer) ListenAndServeTLS(certFile, keyFile string) error {
	wc.locker.Lock()
	defer wc.locker.Unlock()
	wc.server.Handler = http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
		wc.wh.handler(WebContext{request: request, response: writer})
	})
	return wc.server.ListenAndServeTLS(certFile, keyFile)
}

func (wc *webContainer) addRoute(method Method, path string, whf webHandlerFunc) {
	wc.locker.Lock()
	defer wc.locker.Unlock()
	wc.routes[methodPath{method: method, path: path}] = whf
}

// simpleHandlerWrapper
type simpleHandlerWrapper struct {
	wc *webContainer
}

func (h *simpleHandlerWrapper) handler(whf WebContext) {
	var (
		wc     = h.wc
		routes = wc.routes
	)

	mp := methodPath{
		method: Method(whf.request.Method),
		path:   whf.request.RequestURI,
	}
	fmt.Printf("[%s] - %s - [%s]\n", time.Now().Format(time.RFC3339), mp.method, mp.path)
	route, ok := routes[mp]
	if !ok {
		mp.path = "*"
		route, ok = routes[mp]
		if !ok {
			whf.response.WriteHeader(http.StatusNotFound)
			return
		}
	}
	route(whf)
}
